"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 硬件故障类--磁盘
Case Name   : 归档日志超大导致磁盘空间占满
Create At   : 2020-10-15
Owner       : n@ningyali
Description :
    1、注入故障，将磁盘空间写到剩余1024M
    2、打开归档开关，将archive_timeout设置为10s
    3、重复执行DML操作，查看归档日志大小
    4、清除故障，执行DML操作
Expect      :
    1、设置成功
    2、反复执行DML操作，将导致占用巨大的归档存储空间
    3、归档存储空间占满磁盘后数据库无法写入
    4、清除故障后，数据库可写入
History     :
"""

import unittest

from yat.test import Node
from yat.test import macro

from testcase.utils.Common import *
from testcase.utils.CommonSH import *


class BigArchFileAndDiskFull(unittest.TestCase):
    primary_sh = CommonSH('PrimaryDbUser')
    standby1_sh = CommonSH('Standby1DbUser')
    nodes_tuple = ('PrimaryDbUser', 'Standby1DbUser', 'Standby2DbUser')

    @RestartDbCluster(*nodes_tuple)
    def setUp(self):
        self.log = Logger()
        self.log.info('--------------Opengauss_Reliability_Hardware_Case012:开始执行----------------')
        self.primary_root_node = Node(node='PrimaryRoot')
        self.primary_user_node = Node(node='PrimaryDbUser')
        self.standby1_root_node = Node(node='Standby1Root')
        self.standby1_user_node = Node(node='Standby1DbUser')
        self.standby2_root_node = Node(node='Standby2Root')
        self.standby2_user_node = Node(node='Standby2DbUser')
        self.com = Common()
        self.Constant = Constant()
        self.t_name = 'test_hardware012'
        self.arch_path = os.path.join(os.path.dirname(os.path.dirname(macro.DB_INSTANCE_PATH)), 'arch')

        self.log.info("-----------获取主节点数据目录所在磁盘 -----------")
        cmd = f"df -h {macro.DB_INSTANCE_PATH}"
        self.log.info(cmd)
        msg = self.primary_root_node.sh(cmd).result()
        self.log.info(msg)
        self.db_disk_name = msg.splitlines()[-1].split()[0].strip()

    def test_main(self):
        self.log.info("---------------创建归档目录----------------")
        shell_cmd = f"mkdir -p {self.arch_path}; ls {self.arch_path}"
        self.log.info(shell_cmd)
        msg = self.primary_user_node.sh(shell_cmd).result()
        self.log.info(msg)
        self.assertNotIn(self.Constant.NO_FILE_MSG, msg)

        avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
        self.log.info('Available:' + avail_size)

        self.log.info("-------------打开归档开关，将archive_timeout设置为10s---------------")
        # 为了在备机上开启只读查询，wal_level必须在主机上设置成hot_standby ，并且备机必须打开hot_standby参数。
        msg = self.primary_sh.executeGsguc('set', self.Constant.GSGUC_SUCCESS_MSG, 'wal_level=hot_standby')
        self.assertTrue(msg)
        # on表示进行归档。off表示不进行归档。
        msg = self.primary_sh.executeGsguc('set', self.Constant.GSGUC_SUCCESS_MSG, 'archive_mode=on')
        self.assertTrue(msg)
        # 设置归档路径，--remove-destination选项作用为：拷贝前如果目标文件已存在，会先删除已存在的目标文件，然后执行拷贝操作。
        msg = self.primary_sh.executeGsguc('set', self.Constant.GSGUC_SUCCESS_MSG,
                                           f"archive_command='cp --remove-destination %p {self.arch_path}/%f'")
        self.assertTrue(msg)

        msg = self.primary_sh.executeGsguc('set', self.Constant.GSGUC_SUCCESS_MSG, 'archive_timeout=10')
        self.assertTrue(msg)
        msg = self.primary_sh.executeGsguc('set', self.Constant.GSGUC_SUCCESS_MSG, 'checkpoint_segments=10')
        self.assertTrue(msg)

        self.log.info("-------------重启集群，使以上设置生效---------------")
        is_stopped = self.primary_sh.stopDbCluster()
        self.assertTrue(is_stopped)
        is_started = self.primary_sh.startDbCluster()
        self.assertTrue(is_started)

        self.log.info("-------------切换到一个新的事务日志文件---------------")
        sql_cmd = "select pg_switch_xlog();"
        self.log.info(sql_cmd)
        msg = self.primary_sh.executDbSql(sql_cmd)
        self.log.info(msg)

        self.log.info("--------------建表---------------")
        create_table_cmd = f'''drop table if exists {self.t_name}; 
                                create table {self.t_name}(id int,name varchar(50),name1 varchar(50),name2 varchar(50));'''
        self.log.info(create_table_cmd)
        msg = self.primary_sh.executDbSql(create_table_cmd)
        self.log.info(msg)
        for error_msg in self.Constant.SQL_WRONG_MSG:
            self.assertNotIn(error_msg, msg)

        self.log.info("-------------------注入磁盘满故障--------------------")
        cfe_command = f'rfile_full (diskname) values ({self.db_disk_name})'
        cfe_msg = self.com.cfe_inject(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_FULL_SUCCESS_MSG, cfe_msg)

        self.log.info("--------------持续写入数据到数据库使磁盘占满---------------")
        for i in range(1, 100000):
            self.log.info("----------------查看磁盘剩余可用空间-----------------")
            shell_cmd = f" df {macro.DB_INSTANCE_PATH}"
            self.log.info(shell_cmd)
            msg = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(msg)

            self.log.info("----------------查看归档存储空间-----------------")
            shell_cmd = f" du {self.arch_path}"
            self.log.info(shell_cmd)
            msg = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(msg)

            self.log.info(f"----------------第{i}次：重复执行DML操作-------------------")
            insert_cmd = f'''
            start transaction;
            insert into {self.t_name} values (generate_series(1,10000), '测试{i}', '可靠性测试reliability_hardware012', '持续写入数据');
            commit;
            '''
            dml_result = self.primary_sh.executDbSql(insert_cmd)
            self.log.info('sqlexecute result: ' + dml_result)

            self.log.info("----------------查看磁盘剩余可用空间-----------------")
            avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
            self.log.info('Available:' + avail_size)

            if self.Constant.DISK_FULL_MSG in dml_result or avail_size == '0':
                self.log.info('磁盘已满')
                break

        self.log.info("---------------归档存储空间占满磁盘后数据库无法写入----------------")
        insert_cmd = f"insert into {self.t_name} values (10001, '测试100001', '可靠性测试reliability_hardware012', '磁盘满后写入');	"
        dml_result = self.primary_sh.executDbSql(insert_cmd)
        self.log.info('sqlexecute result: ' + dml_result)
        # 磁盘满可能导致数据库主节点stop，继而连接数据库失败
        if self.Constant.FAILED_CONNECT_DB not in dml_result:
            self.assertIn(self.Constant.DISK_FULL_MSG, dml_result)

        self.log.info("----------------------------清除故障-----------------------------")
        cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
        cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

        self.log.info("----------------查看磁盘剩余可用空间-----------------")
        avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
        self.log.info('Available:' + avail_size)
        self.assertGreater(int(avail_size), 0)

        self.log.info("---------------清除故障后数据库可写入--------------")
        sql_cmd = f'''insert into {self.t_name} values (1000002,'reliability_hardware012', '开始执行{time.strftime(
            "%Y%m%d_%H:%M:%S", time.localtime())}');'''
        self.log.info(sql_cmd)
        msg = self.primary_sh.executDbSql(sql_cmd)
        self.log.info(msg)
        for error_msg in self.Constant.SQL_WRONG_MSG:
            self.assertNotIn(error_msg, msg)

    def tearDown(self):
        self.log.info("----------------------------清除故障-----------------------------")
        cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
        cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

        self.log.info("--------------删除表---------------")
        drop_table_cmd = f'''drop table if exists {self.t_name}; '''
        self.log.info(drop_table_cmd)
        msg = self.primary_sh.executDbSql(drop_table_cmd)
        self.log.info(msg)

        self.log.info("----------------查看磁盘剩余可用空间-----------------")
        avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
        self.log.info('Available:' + avail_size)

        self.log.info("-------------还原默认值---------------")
        # wal_level默认值为hot_standby，无需还原;
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, 'archive_mode=off')
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f"archive_command")
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, 'archive_timeout=60')
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, 'checkpoint_segments=64')

        self.log.info("-------------切换到一个新的事务日志文件---------------")
        sql_cmd = "select pg_switch_xlog();"
        self.log.info(sql_cmd)
        msg = self.primary_sh.executDbSql(sql_cmd)
        self.log.info(msg)

        self.log.info("-----------删除归档目录-----------")
        shell_cmd = f"rm -rf {self.arch_path}; ls -l {self.arch_path}"
        self.log.info(shell_cmd)
        msg = self.primary_root_node.sh(shell_cmd).result()
        self.log.info(msg)

        self.log.info('---------------Opengauss_Reliability_Hardware_Case012:执行完成---------------')
